<template>
  <div class="hello">
    <div style="display: flex">
      <div style="cursor: pointer" @click="openFile"><slot></slot></div>
      <div style="flex: 1"></div>
    </div>
    <input
      style="display: none"
      type="file"
      :ref="'input' + id"
      @change="openCanvas"
    />
    <el-dialog title="编辑图片" :visible.sync="visible" :modal="false">
      <div
        :style="{ width: width + 'px', height: height + 'px' }"
        class="canvasBox"
      >
        <canvas
          @mousedown="beginMove"
          @mousemove="endMove"
          @mouseup="endMove"
          @mouseout="endMove"
          @mousewheel="changeScale"
          :ref="'canvas' + id"
          :width="width"
          :height="height"
        ></canvas>
      </div>
      <div class="btns">
        <el-button type="primary" size="mini" @click="submit">{{
          uploading ? "上传中..." : "确定"
        }}</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import api from "../api/index";
export default {
  name: "uploadCanvas",
  props: {
    action: String, //上传路径
    onSuccess: Function, //成功回调
    fileKey: String, //图片关键字
    width: {
      //目标宽度
      type: Number,
      default: 300,
    },
    height: {
      //目标高度
      type: Number,
      default: 300,
    },
    headers: Object,
    cicle: Boolean, //是否圆形裁剪
  },
  data() {
    return {
      imgUrl: "", //上传到库的图片地址
      id: new Date().getTime(), //使用时间戳作为每个上传控件的唯一标准
      visible: false, //弹框开关
      canvas: "", //dom元素
      ctx: "", //画布对象
      scale: 0.5, //缩放
      x: 0, //位移x
      y: 0, //位移y
      image: "", //选中图片
      moveing: false, //移动状态
      downX: 0, //mousedown后存储鼠标位置x
      downY: 0, //mousedown后存储鼠标位置y
      startX: 0, //mousedown后存储位移x
      startY: 0, //mousedown后存储位移y
      uploading: false, //上传状态
    };
  },
  methods: {
    upload(file) {
      this.visible = true;
      //创建表单数据
      let formData = new FormData();
      //最近上传内容
      formData.append(this.fileKey || "file", file);
      //开启上传状态
      this.uploading = true;
      //使用axios上传文件
      this.$http({
        method: "POST",
        url: "/upload",
        data: formData,
        headers: this.headers, //必须设置
      }).then((res) => {
        //关闭上传状态
        this.uploading = false;
        //关闭弹窗
        this.visible = false;
        //回调成功
        this.changeAvatar(res);
      });
    },
    changeAvatar(res) {
      if (res.path) this.imgUrl = api.baseURL + "/" + res.path;
      this.imgUrl = this.imgUrl.replaceAll("\\", ("\\\\\\\\"));
      let userName = localStorage.getItem('userName')
      this.$http({
        method: "POST",
        url: "/admin/changeAvatar",
        data: { img: this.imgUrl,
        userName
        },
        headers: this.headers, //必须设置
      }).then((res) => {
        if (!res.errorMsg) {
          localStorage.setItem("avatar", res.photo);
          this.$emit("load");
          this.$message({
            type: "success",
            message: "数据处理成功",
          });
        }
      });
    },
    openFile() {
      //调用浏览器选择文件
      this.$refs["input" + this.id].value = "";
      this.$refs["input" + this.id].click();
    },
    //文件类型转DataURL
    fileTobase64(file, callback) {
      //创建文件实例
      var f = new FileReader();
      //监听加载完成
      f.onload = function (e) {
        callback(e.target.result);
      };
      //读取文件
      f.readAsDataURL(file);
    },
    openCanvas() {
      //获取所选择的文件列表
      let files = this.$refs["input" + this.id].files;
      //直接上传文件
      //this.upload(files[0])
      //编辑图片后上传
      //file类型转换为base64
      this.fileTobase64(files[0], (base64) => {
        //开启弹框
        this.visible = true;
        //延迟执行
        this.$nextTick(() => {
          //保存canvas元素
          this.canvas = this.$refs["canvas" + this.id];
          //保存画布操作对象
          this.ctx = this.canvas.getContext("2d");
          //保存所选图片
          this.image = new Image(); //创建图片对象
          //监听图片加载完成
          this.image.onload = () => {
            //开始渲染
            this.drawImage();
          };
          //设置图片路径
          this.image.src = base64;
        });
      });
    },
    //修改缩放比例实现放大缩小
    changeScale(event) {
      //滚动鼠标滑轮，通过wheelDelta值区分方向
      this.scale += event.wheelDelta > 0 ? 0.01 : -0.01;
      //排除小于0的情况
      if (this.scale <= 0) this.scale = 0;
      //渲染
      this.drawImage();
    },
    //mousedowm事件触发
    beginMove(event) {
      //记录点击位置
      this.downX = event.pageX;
      this.downY = event.pageY;
      //记录图片位置
      this.startX = this.x;
      this.startY = this.y;
      //保存为移动状态
      this.moveing = true;
    },
    endMove(event) {
      //鼠标移动事件且在移动中
      if (this.moveing && event.type == "mousemove") {
        //从新计算位置
        this.x = this.startX + (event.pageX - this.downX);
        this.y = this.startY + (event.pageY - this.downY);
        //渲染
        this.drawImage();
      } else {
        //其他事件类型退出移动状态
        if (this.moveing) this.moveing = false;
      }
    },
    drawImage() {
      //通过设置画布元素的宽高重置画布
      this.canvas.width = this.width;
      this.canvas.height = this.height;
      //是否按圆形裁剪
      if (this.cicle) {
        //绘制裁剪区域
        this.ctx.beginPath();
        //定义圆形区域
        this.ctx.arc(
          this.width / 2,
          this.height / 2,
          Math.min(this.width, this.height) / 2,
          0,
          2 * Math.PI
        );
        //裁剪
        this.ctx.clip();
      }
      //渲染图片
      this.ctx.drawImage(
        this.image,
        this.x,
        this.y,
        this.image.width * this.scale,
        this.image.height * this.scale
      );
    },
    //base64转换为blob
    base64ToBlob(base64) {
      var arr = base64.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], {
        type: mime,
      });
    },
    submit() {
      //canvas转为base64
      let base64 = this.canvas.toDataURL("image/png");
      //base64转换为blob
      let blob = this.base64ToBlob(base64);
      //上传blob
      this.upload(blob);
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.canvasBox {
  margin: 0 auto;
  background: #000;
}
.btns {
  text-align: right;
  margin-top: 32px;
}
</style>